﻿//-----------------------------------------------------------------------
// <copyright file="FxCopUtility.cs">
//     Copyright (c) Nicole Calinoiu. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

using Microsoft.FxCop.Sdk;

namespace Bordecal.FxCop.Sdk
{
	/// <summary>
	/// Helper class for common rule functionality.
	/// </summary>
	public static class FxCopUtility
	{
		/// <summary>
		/// Gets the expressions assigned to elements of a parameter array within a code block.
		/// </summary>
		/// <param name="array">The parameter array.</param>
		/// <param name="assigningBlock">The block within which to search for element assignments.</param>
		/// <returns>A dictionary of assigned elements, where the keys correspond to positions within the array.</returns>
		public static IDictionary<int, Expression> GetParameterArrayElements(Expression array, Block assigningBlock)
		{
			if (array == null)
			{
				throw new ArgumentNullException("array");
			}

			if (assigningBlock == null)
			{
				throw new ArgumentNullException("assigningBlock");
			}

			ArrayType arrayType = array.Type as ArrayType;
			if (arrayType == null)
			{
				throw new ArgumentException("Expression type is not an array.", "array");
			}

			if (arrayType.Rank != 1)
			{
				throw new ArgumentException("Only one-dimensional arrays are supported.", "array");
			}

			ArrayAssignmentVisitor visitor = new ArrayAssignmentVisitor(array);
			visitor.VisitBlock(assigningBlock);

			return visitor.AssignedElements;
		}

		#region Nested types

		[SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented",
			Justification = "False positive.")]
		private sealed class ArrayAssignmentVisitor : BinaryReadOnlyVisitor
		{
			private readonly Expression _array;
			private readonly IDictionary<int, Expression> _assignedElements = new Dictionary<int, Expression>();

			internal ArrayAssignmentVisitor(Expression array)
			{
				this._array = array;
			}

			internal IDictionary<int, Expression> AssignedElements
			{
				get
				{
					return this._assignedElements;
				}
			}

			public override void VisitAssignmentStatement(AssignmentStatement assignment)
			{
				if (assignment == null)
				{
					throw new ArgumentNullException("assignment");
				}

				if (assignment.Target.NodeType == NodeType.Indexer)
				{
					Indexer indexer = (Indexer)assignment.Target;
					if (indexer.Object == this._array)
					{
						this.AssignedElements[(int)((Literal)indexer.Operands[0]).Value] = assignment.Source;
					}
				}

				base.VisitAssignmentStatement(assignment);
			}
		}

		#endregion
	}
}